Evaluación de cortocircuito

De Wikipedia, la enciclopedia libre

La evaluación de corto-circuito,[1]evaluación mínima, o evaluación McCarthy (por el científico John McCarthy) denota la semántica de algunos operadores booleanos en algunos lenguajes de programación en los cuales el segundo argumento no se ejecuta o evalúa si el primer argumento de la función AND evalúa y el resultado es falso, el valor total tiene que ser falso; y cuando el primer argumento de la función OR evalúa y el resultado es verdadero, el valor total tiene que ser verdadero. En algunos lenguajes de programación, como Lisp, los operadores booleanos usuales son corto-circuito. En otros (Java, Ada), tanto los corto-circuito y los operadores booleanos están disponibles. Para algunos operadores booleanos, como XOR, no le es posible evaluar por corto-circuito, porque ambos operadores están obligados a determinar el resultado.

La expresión de corto-circuito x Sand y (usando Sand para denotar la variedad de corto-circuito) es equivalente a la sentencia condicional if x then y else false; la expresión x Sor y es equivalente a if x then true else y.

Los operadores corto-circuito son estructuras de control en vez de simples operadores aritméticos, al igual que no son strict. ALGOL 68 usó "Proceduring" para lograr operadores y procedimientos de corto-circuito definidos por los usuarios.

En lenguajes aproximados o imprecisos, los cuales tienen más de dos valores de verdad Verdadero y Falso, los operadores corto-circuito podrían devolver la última subexpresión evaluada, de esta manera x Sor y y x Sand y son equivalentes a if x then x else y y if x then y else x respectivamente (sin evaluar x dos veces). Esto es llamado "el último valor" en la tabla de abajo.

En lenguajes que emplean una evaluación perezosa por defecto, como Haskell, todas las funciones son efectivamente de "corto-circuito", y no son necesarios operadores especiales de corto-circuito.

Soporte en lenguajes comunes de programación[editar]

Operadores booleanos en varios lenguajes
Lenguaje Operadores ansiosos Operadores de corto-circuito Tipo de resultado
ABAP none and, or Boolean1
Ada, Eiffel and, or and then, or else Boolean
ALGOL 68 and, &, ∧; or, ∨ andf , orf Boolean
C2 none &&, ||, ?[2] Numeric (&&,||), opnd-dependent (?)
C++3 &, | &&, ||, ?[3] Boolean (&&,||), opnd-dependent (?)
Go, OCaml, Haskell none &&, || Boolean
C#, Java,

MATLAB R

&, | &&, || Boolean
ColdFusion none AND, OR, &&, || Boolean
Erlang and, or andalso, orelse Boolean
Fortran .and., .or. Boolean
JavaScript none &&, || Last value
Lisp, Lua, Scheme none and, or Last value
Modula-2 none AND, OR Boolean
Oberon none &, OR Boolean
Pascal and, or4 and_then, or_else5 Boolean
Perl, Ruby &, | &&, and, ||, or Last value
PHP none &&, and, ||, or Boolean
Python none and, or Last value
Smalltalk &, | and:, or: Boolean
Standard ML andalso, orelse Boolean
Visual Basic .NET And, Or AndAlso, OrElse Boolean
VB Script, VB Classic, VBA And, Or Select Case Numeric

1ABAP en realidad no posee una distinción de los tipos de booleanos.
2 C, antes de C99, en realidad no tenía una distinción de los tipos de booleanos; los operadores lógicos devuelven 0 (para Falso) o 1 (para Verdadero).
3 Cuando es sobrecargado, los operadores && y || son ansiosos y pueden devolver cualquier tipo.
4 ISO Pascal permite pero no requiere corto-circuito.
5 ISO-10206 extendió a Pascal el soporte a and_then and or_else.[4]

Uso común[editar]

Evitando los efectos colaterales de ejecución en las expresiones secundarias[editar]

Ejemplo común.

int denom = 0;
if (denom && num/denom) {
        ... // asegura que no se produzca un error al calcular num/denom es decir una división por cero    
}

Considere el siguiente ejemplo usando lenguaje Lenguaje C:

int a = 0;
if (a && myfunc(b)) {
    do_something();
}

En este ejemplo, la evaluación de corto-circuito garantiza que myfunc(b) nunca será llamado. Esto se debe a que la evaluación de a es falso. Esta característica permite la programación de dos constructores. Inicialmente si se revisa la primera subexpresión, sin importar si es necesario un costo computacional y el proceso evaluado es falso, uno puede eliminar el costo computacional en el segundo argumento. Luego permite una construcción donde la primera expresión garantiza una condición sin la cual la segunda expresión pueda causar un error de ejecución run-time error. Ambos están ilustrados en el siguiente fragmento de código C donde una evaluación mínima previene una desferenciación a un apuntador a null y un desbordamiento de memoria.

bool is_first_char_valid_alpha_unsafe(const char *p)
{
  return isalpha(p[0]); // Fallo de Segmentación altamente posible con p==NULL
}

bool is_first_char_valid_alpha(const char *p)
{
  return p != NULL && isalpha(p[0]); // a) es innecesario la ejecución de isalpha() cuando p == NULL, b) No hay riesgo de Fallo de Segmentación
}

Posibles problemas[editar]

Segunda condición sin verificar dirige a un efecto colateral no ejecutado[editar]

A pesar de estos beneficios una evaluación mínima puede causar problema para los programadores que no se den cuenta (o se olviden) que está pasando. Por ejemplo, en el código

if (expressionA && myfunc(b)) {
    do_something();
}

Si myfunc(b) pretende realizar alguna operación requerida sin tener en cuenta si do_something() es ejecutado, tal como la asignación de recursos al sistema, y expressionA es evaluado como Falso, entonces myfunc(b) no se ejecutará lo cual puede causar problemas. Algunos lenguajes de programación tales como Java, tienen dos operadores, uno que emplea la evaluación mínima y el otro que no, para evitar este problema

Problemas con las declaraciones incumplidas de efectos secundarios pueden ser resueltos fácilmente con el estilo de programación adecuada, es decir, no usando efectos colaterales en las declaraciones booleanas, así como usando valores con efectos colaterales en las evaluaciones tiende generalmente a producir un código opaco y propenso a errores.[5]

Desde que las evaluaciones mínimas son parte de las definiciones de las operaciones semánticas y no (opcional) de una optimización, muchos estilos de codificación se basan en él como un constructor condicional conciso, tal como el lenguaje Perl:

some_condition or die; # Cancelar la ejecución si some_condition es falso
some_condition and die; # Cancelar la ejecución si some_condition es verdadero

Eficiencia de la codificación[editar]

Si ambas expresiones usadas como condiciones son simples variables booleanas, puede ser mucho más rápido de evaluar las dos condiciones usadas en las operaciones booleanes de una vez, como si requiriera un simple ciclo de cálculo, en contraposición a uno o dos ciclos usados en las evaluaciones de los circuitos cortos (dependiendo del valor del primero). La diferencia en términos de eficiencia computacional entre estos dos casos depende fuertemente del compilador y del esquema de optimización utilizado; con la correcta optimización estos serán capaces de ejecutarse a la misma velocidad, así como serán compilados a un código máquina idéntico.[6]

Un corto-circuito puede llevar a errores en predictor de saltos en los modernos unidaded centrales de procesamiento, y reducir dramáticamente el desempeño (un ejemplo claro es un rayo altamente optimizado con el eje de la caja del código de intersección alineado en el trazado de rayos alineado trazado de rayos). Algunas compilaciones pueden detectar tales casos y emitir una codificación más rápida, pero no siempre es posible debido a violaciones de segmento del lenguaje C. Un código altamente optimizado debería emplear otras formas de hacer esto (como el manual de uso del código de máquina).

Referencias[editar]